package com.zj.eswrapper.wrapper;

import cn.hutool.core.collection.CollectionUtil;
import com.zj.eswrapper.interfaces.IEsAggNestWrapper;
import com.zj.eswrapper.interfaces.impl.AbstractEsAggWrapper;
import com.zj.eswrapper.model.Order;
import com.zj.eswrapper.model.Range;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.range.RangeAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.range.RangeAggregator;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.*;
import org.elasticsearch.search.builder.SearchSourceBuilder;

import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author 19242
 */
public abstract class AbstractEsLastAggWrapper<Field,
        AggWrapper extends AbstractEsLastAggWrapper<Field, AggWrapper, QueryWrapper>,
        QueryWrapper extends AbstractEsLastQueryWrapper<Field, QueryWrapper, ?, AggWrapper>>
        extends AbstractEsAggWrapper<Field, AggWrapper, QueryWrapper, SearchSourceBuilder, AggregationBuilder> {

    public AbstractEsLastAggWrapper() {
    }

    public AbstractEsLastAggWrapper(AbstractEsLastAggWrapper<?, ?, ?> aggWrapper) {
        super(aggWrapper);
    }

    private final IEsAggNestWrapper<AggWrapper> aggNestWrapper = new IEsAggNestWrapper<AggWrapper>() {
        @Override
        public IEsAggNestWrapper<AggWrapper> agg(Consumer<AggWrapper> consumer) {
            if (Objects.isNull(consumer)) {
                return this;
            }
            AggWrapper wrapper = createWrapper();
            consumer.accept(wrapper);
            return agg(wrapper);
        }

        @Override
        public IEsAggNestWrapper<AggWrapper> agg(AggWrapper child) {
            if (Objects.isNull(child) || Objects.isNull(agg.getBuilder())) {
                return this;
            }
            agg.getBuilder().subAggregation(child.agg.getBuilder());
            return this;
        }
    };

    @Override
    public IEsAggNestWrapper<AggWrapper> terms(String name, Field field, Integer size, Order... orders) {
        TermsAggregationBuilder builder = AggregationBuilders.terms(name).field(getField(field));
        if (Objects.nonNull(size)) {
            builder.size(size);
        }
        if (Objects.nonNull(orders) && orders.length > 0) {
            List<BucketOrder> orderList = Stream.of(orders)
                    .filter(Objects::nonNull)
                    .map(order -> BucketOrder.aggregation(order.getColumn(), order.isAsc()))
                    .collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(orderList)) {
                builder.order(orderList);
            }
        }
        this.agg.setBuilder(builder);
        return aggNestWrapper;
    }

    @Override
    public IEsAggNestWrapper<AggWrapper> count(String name, Field field) {
        ValueCountAggregationBuilder builder = AggregationBuilders.count(name).field(getField(field));
        this.agg.setBuilder(builder);
        return aggNestWrapper;
    }

    @Override
    public IEsAggNestWrapper<AggWrapper> cardinality(String name, Field field) {
        CardinalityAggregationBuilder builder = AggregationBuilders.cardinality(name).field(getField(field));
        this.agg.setBuilder(builder);
        return aggNestWrapper;
    }

    @Override
    public IEsAggNestWrapper<AggWrapper> avg(String name, Field field) {
        AvgAggregationBuilder builder = AggregationBuilders.avg(name).field(getField(field));
        this.agg.setBuilder(builder);
        return aggNestWrapper;
    }

    @Override
    public IEsAggNestWrapper<AggWrapper> sum(String name, Field field) {
        SumAggregationBuilder builder = AggregationBuilders.sum(name).field(getField(field));
        this.agg.setBuilder(builder);
        return aggNestWrapper;
    }

    @Override
    public IEsAggNestWrapper<AggWrapper> max(String name, Field field) {
        MaxAggregationBuilder builder = AggregationBuilders.max(name).field(getField(field));
        this.agg.setBuilder(builder);
        return aggNestWrapper;
    }

    @Override
    public IEsAggNestWrapper<AggWrapper> min(String name, Field field) {
        MinAggregationBuilder builder = AggregationBuilders.min(name).field(getField(field));
        this.agg.setBuilder(builder);
        return aggNestWrapper;
    }

    @Override
    public IEsAggNestWrapper<AggWrapper> stats(String name, Field field) {
        StatsAggregationBuilder builder = AggregationBuilders.stats(name).field(getField(field));
        this.agg.setBuilder(builder);
        return aggNestWrapper;
    }

    @Override
    public IEsAggNestWrapper<AggWrapper> filter(String name, Consumer<QueryWrapper> consumer) {
        if (Objects.isNull(consumer)) {
            return aggNestWrapper;
        }
        QueryWrapper wrapper = createQueryWrapper();
        consumer.accept(wrapper);
        QueryBuilder builder = wrapper.queryBuilder();
        if (Objects.isNull(builder)) {
            return aggNestWrapper;
        }
        FilterAggregationBuilder filter = AggregationBuilders.filter(name, builder);
        this.agg.setBuilder(filter);
        return aggNestWrapper;
    }

    @Override
    public IEsAggNestWrapper<AggWrapper> range(String name, Field field, Collection<Range> ranges) {
        RangeAggregationBuilder builder = AggregationBuilders.range(name).field(getField(field));
        if (CollectionUtil.isNotEmpty(ranges)) {
            for (Range range : ranges) {
                builder.addRange(new RangeAggregator.Range(range.getKey(), range.getFrom(), range.getTo()));
            }
        }
        this.agg.setBuilder(builder);
        return aggNestWrapper;
    }

    @Override
    public void addAgg(SearchSourceBuilder builder) {
        if (Objects.isNull(builder) || Objects.isNull(agg.getBuilder())) {
            return;
        }
        builder.aggregation(agg.getBuilder());
    }

    @Override
    public IEsAggNestWrapper<AggWrapper> custom(String name, AggregationBuilder builder) {
        if (Objects.isNull(builder)) {
            return aggNestWrapper;
        }
        this.agg.setBuilder(builder);
        return aggNestWrapper;
    }
}
